﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using Glucometer.ModuleHandler;
using Glucometer.Utility;

namespace Glucometer
{
    public enum OperationMode
    { 
        TESTING,
        BROWSING,
        UPLOADING,
        SETUP,
        NONE
    }

    public enum BeeperStyle
    {
        SHORT_BEEP,
        LONG_BEEP,
        DOUBLE_BEEP,
        SHORT_LONG_BEEP
    }

    public enum ErrorCode
    {
        INITIALIZATION_ERROR,
        INVALIDSTRIP_ERROR,
        METERSTATUSCHECK_ERROR,
        INSUFFICIENTBLOOD_ERROR,
        TESTING_ERROR,
        NONE,
        MODE_VALIDATION_ERROR,
        UNKNOWN
    }

    public enum EndingType
    { 
        VOLUNTARY,
        AUTO,
        ERROR
    }

    class GlucometerController
    {
        private IHarware _hardware;

        private HardWareInterruptInterpreter _hardwareInterruptProcessor;

        private OperationMode _currentMode;

        private DeviceSettings _settings;

        private DeviceStatus _deviceStatus;

        private DeviceMemory _deviceMemory;

        private Battery _battery;

        private Dictionary<OperationMode, GlucometerModuleHandler> _moduleHandlerList;

        public GlucometerController(IHarware hardware, 
                                    HardWareInterruptInterpreter interruptProcessor, 
                                    DeviceSettings settings, 
                                    DeviceMemory memory, 
                                    DeviceStatus status,
                                    Battery battery,
                                    Dictionary<OperationMode,GlucometerModuleHandler> moduleHandlerList)
        {

            this._hardwareInterruptProcessor = interruptProcessor;

            this._hardware = hardware;

            this._settings = settings;

            this._battery = battery;

            settings.DateChanged += new d_EmptyArgument(settings_DateChanged);

            this._hardwareInterruptProcessor.SoftwareEventOccurred += new d_SoftwareEvents(SoftwareEventOccurred);

            this._currentMode = OperationMode.NONE;

            this._deviceStatus = status;

            this._deviceMemory = memory;

            this._moduleHandlerList = moduleHandlerList;

            GlucometerModuleHandler.DeviceEnding += new d_EmptyArgument(GlucometerModuleHandler_DeviceEnding);

            GlucometerModuleHandler.DeviceStarting +=new d_EmptyArgument(GlucometerModuleHandler_DeviceStarting);

            GlucometerModuleHandler.InitializeIdleTimerCounter();
        }

        void settings_DateChanged()
        {
            //WHEN THE DATETIME IS CHANGED ASK UI TO UPDATE IT
            //ONLY WHEN IT IS POWERED ON AND IS NOT IN SETUP MODE
            if(!_deviceStatus.PoweredOff && _currentMode != OperationMode.SETUP)
                _hardware.updateDateTime(_settings.getDateTime());
        }

        void GlucometerModuleHandler_DeviceEnding()
        {
            //IF THE CURRENT MODE IS SETUP THEN PERSIST THE SETTINGS

            //IF THE CURRENT MODE IS TESTING THEN PERSIST THE RECORDS

            //SET CURRENT MODE TO NONE
            _currentMode = OperationMode.NONE;

            //HANDLE THE UI DISPLAY
            _hardware.SetDeviceMode(OperationMode.NONE);

            //STOP USING THE BATTERY
            _battery.UseBattery = false;

            //HIDE BATTERY DISPLAY
            _hardware.ShowBatteryLevel(-1);

            //HIDE AC SYMBOL
            _hardware.ShowAcSymbol(false);

            //CLEAR DISPLAY
            _hardware.ShowDisplay("");
        }

        void GlucometerModuleHandler_DeviceStarting()
        { 
            //HANDLE THE UI DISPLAY HERE
            //START USING THE BATTERY
            _battery.UseBattery = true;

            //SHOW THE DATE TIME
            _hardware.updateDateTime(_settings.getDateTime());

            //SHOW THE BATTERY
            _hardware.ShowBatteryLevel(_battery.BatteryLevel);

            //IF THE BATTERY LEVEL IS LESS THAN OR EQUAL TO 1 THEN BLINK THE BATTERY
            if (_battery.BatteryLevel <= 1)
                _hardware.BlinkComponent(DisplayComponent.BATTERY);

            //SHOW THE AC SYMBOL
            _hardware.ShowAcSymbol(_deviceStatus.acPluggedIn);
        }

        void SoftwareEventOccurred(SoftwareEventsType set)
        {
            try
            {
                //UPDATE THE DEVICE STATUS BASED ON THE EVENT
                _deviceStatus.UpdateStatusByEvent(set);

                //ON EACH TIME TICK EVENT TRY TO UPDATE THE DATE/TIME
                if (set == SoftwareEventsType.TIME_TICK)
                {
                    _settings.updateTick();
                    return;
                }

                //IF THE AC IS PLUGGED IN THEN CHARGE THE BATTERY AND SHOW THE AC SYMBOL IF DEVICE POWERED ON
                if (set == SoftwareEventsType.AC_PLUGGED_IN)
                {
                    _battery.ChargeBattery = true;
                    if(!_deviceStatus.PoweredOff)
                        _hardware.ShowAcSymbol(true);
                    return;
                }

                //IF THE AC IS PLUGGED OUT THEN STOP CHARGING THE BATTERY AND 
                //HIDE THE AC SYMBOL
                if (set == SoftwareEventsType.AC_PLUGGED_OUT)
                {
                    _battery.ChargeBattery = false;
                    _hardware.ShowAcSymbol(false);
                    return;
                }

                //IF THE BATTERY LEVEL IS 0 AND POWERED OFF AND AC NOT CONNECTED THEN DONT PROCEED
                if (_battery.BatteryLevel == 0 && _deviceStatus.PoweredOff && !_deviceStatus.acPluggedIn)
                    return;

                //IF THE BATTERY LEVEL DECREASES
                if (set == SoftwareEventsType.BATTERY_LEVEL_DOWN)
                {
                    int level = _battery.BatteryLevel;
                    //UPDATE THE BATTERY LEVEL UI DISPLAY IF DEVICE ON
                    if (!_deviceStatus.PoweredOff)
                        _hardware.ShowBatteryLevel(level);

                    //IF THE LEVEL DECREASED TO 25% FROM 50% GIVE A WARNING BEEP
                    //AND BLINK THE BATTERY LEVEL BELOW 25% 
                    if (level == 1)
                    {
                        Beeper.playBeep(BeeperStyle.DOUBLE_BEEP);
                        _hardware.BlinkComponent(DisplayComponent.BATTERY);
                    }

                    //TURN OFF THE DEVICE IF BATTERY LEVEL 0
                    if (_battery.BatteryLevel == 0)
                        GlucometerModuleHandler.endDevice(EndingType.VOLUNTARY, _currentMode);
                    return;
                }

                //IF THE BATTERY LEVEL INCREASES
                if (set == SoftwareEventsType.BATTERY_LEVEL_UP)
                {
                    //UPDATE THE BATTERY LEVEL UI DISPLAY IF DEVICE ON
                    if(!_deviceStatus.PoweredOff)
                        _hardware.ShowBatteryLevel(_battery.BatteryLevel);
                    return;
                }


                //EVENT HANDLING IN CURRENT MODE
                if (_currentMode != OperationMode.NONE)
                {
                    this._hardware.ShowDisplay(set.ToString());

                    //IF THE MODULE HANDLER IS AVAILABLE THEN ASK IT TO HANDLE EVENT
                    if (_moduleHandlerList.ContainsKey(_currentMode))
                        _moduleHandlerList[_currentMode].handleEvent(set);
                }
                else if (_deviceStatus.PoweredOff)
                {
                    //CHECK IF THE EVENT IS A MODE SELECTION OPERATION
                    //IF IT IS NOT THEN JUST IGNORE IT
                    OperationMode mode = GlucometerModuleHandler.checkModeSelectionProcess(set);
                    if (mode != OperationMode.NONE)
                    {
                        //SET TURN ON
                        this._deviceStatus.PoweredOff = false;                        
                        //HANDLE THE UI IN START UP
                        GlucometerModuleHandler.startDevice();

                        this._deviceStatus.IsWatingForInitialization = mode;
                        SoftwareEventOccurred(set);
                    }
                }
                //IF THE DEVICE IS WAITING FOR INITIALIZATION, DO INITIALIZATION PROCESS
                else if (_deviceStatus.IsWatingForInitialization != OperationMode.NONE)
                {
                    if (GlucometerModuleHandler.Initialization())
                    {
                        _deviceStatus.IsWatingForStatusCheck = _deviceStatus.IsWatingForInitialization;
                        _deviceStatus.IsWatingForInitialization = OperationMode.NONE;

                        //INITIALIZE STATUS CHECK
                        SoftwareEventOccurred(set);
                    }
                    else
                    {
                        //HANDLE INITIALIZATION FAILURE HERE
                        //END THE METER WITH ERROR ENDING PROCEDURE
                        GlucometerModuleHandler.endDevice(EndingType.ERROR, _deviceStatus.IsWatingForInitialization);
                        _deviceStatus.IsWatingForInitialization = OperationMode.NONE;
                    }
                }
                //IF THE DEVICE IS WAITING FOR STATUS CHECK, DO STATUS CHECK PROCESS
                else if (_deviceStatus.IsWatingForStatusCheck != OperationMode.NONE)
                {
                    if (_moduleHandlerList[_deviceStatus.IsWatingForStatusCheck].doStatusCheck(set))
                    {
                        _deviceStatus.IsWaitingForModeValidation = _deviceStatus.IsWatingForStatusCheck;
                        _deviceStatus.IsWatingForStatusCheck = OperationMode.NONE;

                        //INITIATE THE MODE VALIDATION PROCESS
                        SoftwareEventOccurred(set);
                    }
                    else
                    {
                        //HANDLE INITIALIZATION FAILURE HERE
                        //END THE METER WITH ERROR ENDING PROCEDURE
                        GlucometerModuleHandler.endDevice(EndingType.ERROR, _deviceStatus.IsWatingForStatusCheck);
                        _deviceStatus.IsWatingForStatusCheck = OperationMode.NONE;
                    }
                }
                else if (_deviceStatus.IsWaitingForModeValidation != OperationMode.NONE)
                {
                    if (_moduleHandlerList[_deviceStatus.IsWaitingForModeValidation].doModeValidation(set))
                    {
                        //SET THE CURRENT MODE
                        this._currentMode = this._deviceStatus.IsWaitingForModeValidation;

                        //SET WAITING FOR MODE VALIDATION TO NONE
                        this._deviceStatus.IsWaitingForModeValidation = OperationMode.NONE;

                        //SHOW LOG INFO
                        //this._hardware.ShowMode(this._currentMode.ToString() + "  Mode");

                        // REMINDING BEEP BEFORE ENTER MODE
                        GlucometerModuleHandler.playBeep(BeeperStyle.SHORT_BEEP);

                        //START THE MODE
                        _hardware.SetDeviceMode(this._currentMode);

                        //THERE NEEDS TO BE SOMETHING DONE BEFORE THE MODE LIKE BLINKING THE SETUP FIRST EDIT POSITION,
                        //SHOW FIRST RECORD WHILE BROWSING ETC
                        _moduleHandlerList[_currentMode].startDeviceMode();
                    }
                    else
                    {
                        //HANDLE MODE VALIDATION ERROR HERE
                        //BLINK THE MODE SYMBOL IN DISPLAY TO SHOW MODE VALIDATION FAILED
                        _hardware.BlinkMode(_deviceStatus.IsWaitingForModeValidation);

                        //END THE METER WITH ERROR ENDING PROCERE
                        GlucometerModuleHandler.endDevice(EndingType.ERROR, _deviceStatus.IsWaitingForModeValidation);
                        _deviceStatus.IsWaitingForModeValidation = OperationMode.NONE;

                    }
                }
                else
                {
                    //THIS IS THE EVENT THAT IS FIRED WHEN THE DEVICE IS OFF
                    //AND COULDNOT SELECT ANY MODE 
                    //SO JUST IGNORE IT. NOTHING TO DO
                }
            }
            catch { } //NO NEED TO HANDLE 
        }
    }
}